This notebook explains the Potential
Outcomes Framework and estimation of Causal Effects, in particular
the Average
Treatment Effect and related estimators: the Intent-to-Treat
Effect and the Complier
Average Causal Effect (also known as the Local Average Treatment
Effect).
Using a generated population as a reference, we examine the
implications of random sampling and random assignment of treatment,
non-compliance, and consequences of limited sample sizes.
The code needed to run this analysis can be find in the following
GitHub repository
where you can download, comment, or make contributions of your own.
Requirements
First, you will need RStudio to run this notebook. Please download it
here
and then install it.
There are two libraries needed to run this R Notebook: 1. tidyverse (for data
manipulation) 2. randomNames
(to generate the population)
You may experience errors trying to install these packages. Your best
friend, in this case, is Google. Search for the error messages you
receive and try to troubleshoot your way through installing the package.
Unfortunately there isn’t a consistent solution to these problems as
different operating systems and local settings can cause a variety of
issues. However, all these issues should be able to be resolved.
Generating up our population
Lets define and generate the population we’re going to study. Below
we set some general characteristics: - the size of the population, -
gender and ethnicity demographics and - the base level income and
variation (standard deviation) of that income.
population_size <- 1000000
base_income <- 10000
base_income_sd <- 1000
ethnicities <- factor(c("Indian", "Asian", "Black", "Hispanic", "White", "Arabic"))
genders <- factor(c("Female", "Male"))
How many people have we defined to be in our
population?
What is their expected base income?
How many different ethnicities are in our
population?
demographics <- tibble(
gender = sample(genders, population_size, prob = runif(length(genders)),
replace = TRUE),
ethnicity = sample(ethnicities, population_size, prob = runif(length(ethnicities)),
replace = TRUE))
income_male_factor <- runif(1)
income_ethnicity_factor <- runif(1)
population <- randomNames(population_size, return.complete.data = TRUE,
gender = as.numeric(demographics$gender) - 1,
ethnicity = as.numeric(demographics$ethnicity)) %>%
mutate(id = factor(row_number())) %>%
mutate(across(where(is.character), as.factor)) %>%
mutate(income = round(rnorm(population_size, base_income, base_income_sd))) %>%
mutate(income = income + income * (gender * income_male_factor)) %>%
mutate(income = income + income * (ethnicity * income_ethnicity_factor)) %>%
mutate(gender = factor(map_chr(gender, ~ levels(genders)[.x + 1])),
ethnicity = factor(map_chr(ethnicity, ~ levels(ethnicities)[.x])))
summary(population)
gender ethnicity first_name last_name id
Female:939885 Arabic :529797 Joshua : 13492 Martinez: 8997 1 : 1
Male : 60115 Asian : 4500 Michael : 12631 Smith : 6119 2 : 1
Black :121069 Brandon : 10723 Johnson : 5939 3 : 1
Hispanic: 81132 Christopher: 9875 Garcia : 5708 4 : 1
Indian : 27863 Tyler : 9384 Williams: 4963 5 : 1
White :235639 Jacob : 8582 Jones : 4171 6 : 1
(Other) :935313 (Other) :964103 (Other):999994
income
Min. : 10340
1st Qu.: 19668
Median : 28127
Mean : 38746
3rd Qu.: 57852
Max. :179331
population %>%
ggplot(aes(ethnicity, fill = ethnicity)) +
geom_bar()

population %>%
ggplot(aes(gender, fill = gender)) +
geom_bar()

population %>%
ggplot(aes(income, fill = ethnicity, lty = gender)) +
geom_density(alpha = 0.2) +
facet_wrap(~ ethnicity, ncol = 1)

How much more/less income do males make compared to
females?
On average, which ethnicity makes the most?
On average, which ethnicity makes the least?
Are there any significant differences in the proportion of
genders?
Are there any significant differences in the proportion of
ethnicities?
Defining population potential outcomes (God-mode)
Now that we have a population, we will generate the effects that the
policy will have on each individual. Since we have full
knowledge and control of this population, we will know the treatment
effect for each individual by defining both of their potential outcomes
(with and without treatment). The researcher will not know
these values, it is their job to recover these values.
effect_size <- 3000
effect_sd <- 1000
income_time_factor <- runif(1)
population <- population %>%
mutate(potential_0 = income * income_time_factor) %>%
mutate(potential_1 = round(
potential_0 + rnorm(population_size, effect_size, effect_sd)))
population %>%
summarise(across(c(income, potential_0, potential_1), list(`_mean` = mean,
`_stddev` = sd))) %>%
pivot_longer(everything(), names_sep = "__", names_to = c("metric", "agg")) %>%
pivot_wider(names_from = agg)
population %>%
pivot_longer(c(income, potential_0, potential_1)) %>%
ggplot(aes(value, fill = name)) +
geom_density(alpha = 0.2) +
facet_grid(ethnicity ~ gender, scales = "free")

What the mean and standard deviation of the potential
outcomes without treatment?
What the mean and standard deviation of the potential
outcomes with treatment?
What is our actual population average treatment
effect?
What would have happened to incomes if everyone received
treatment?
What would have happened to incomes if no one received
treatment?
Sampling from the population (researcher mode)
As a researcher, we usually don’t have access to the entire
population given a limited research budget. We typically have to choose
a subset of this population. Ideally, we should randomly sample from the
group of people from the population that we would like to study in our
research.
If the policy or research question under consideration applies to
everyone, we’ll want to randomly sample from the entire population. It
could also be that the policy only applies to women, or to certain
ethnicities, in which case there’s no need to sample from the entire
population, only the sub-population (e.g. women or a specific ethnicity)
of concern.
sample_sizes <- 10 ^ seq(1, log(population_size, 10))
population_samples <- map_dfr(sample_sizes, function(sample_size) {
population %>%
sample_n(sample_size) %>%
mutate(sample_size = sample_size)
})
population_samples %>%
ggplot(aes(gender, fill = gender)) +
geom_bar() +
facet_wrap(~ sample_size, scales = "free_x") +
coord_flip()

population_samples %>%
ggplot(aes(ethnicity, fill = ethnicity)) +
geom_bar() +
facet_wrap(~ sample_size, scales = "free_x") +
coord_flip()

population_samples %>%
ggplot(aes(income)) +
geom_histogram(binwidth = 1000) +
facet_wrap(~ sample_size, scales = "free")

What are the different sample sizes we will work with as a
researcher?
What do you notice about the distribution of genders,
ethnicities, and incomes as sample size increases?
How does randomly sampling from the population help in
estimating the treatment effect of our program?
Estimating the Average Treatment Effect (researcher mode)
Randomly sampling from the population helps ensure our study sample
is representative of the population at large. However, randomly sampling
from the population does not help us estimate the effect of our
policy. In order to estimate the effect of our policy we need to compare
Potential Outcomes, specifically the potential outcome when
someone gets treatment and the outcome when they do not get treatment?
The average of the difference between these two potential outcomes is
known as the Average Treatment Effect (ATE).
Does the researcher know the values of both potential
outcomes for the individuals in their sample?
Describe, in a few sentences, how you could estimate the ATE
from the samples we are given?
Full compliance
In order to be able to estimate the ATE, we need to construct two
statistically identical groups with the exception that one of these
groups receives treatment while the others do not. This is the core of
the Randomized
Controlled Trial (RCT).
Certain settings for RCTs ensure that everyone who is in the
treatment group gets treatment while no one in the control group
receives treatment. This is more difficult to ensure in most cases
involving human subjects since human subjects have the freedom to choose
to take up the treatment or not. The ability for those in the treatment
group to deny treatment and those in the control group to take up
treatment is known as non-compliance and the underlying assumption of
which types of this non-compliance exists needs to be evaulated on a
case-by-case basis.
What are the four different types of individuals in our
sample and which of those individuals are assumed to exist and not exist
under full compliance?
First we simulate the RCT with each of the sample sizes from above
with Full Compliance.
The code below simulates random assignment of treatment to half of
the study sample for increasing study sample sizes and tries to
calculate the average treatment effect based on what is observed due to
treatment assignment.
The first row is a summary row taking the average of all of the
individual rows below it. The only thing that changes from table to
table is the number of individuals in that sample, notice the number of
rows in each table in the bottom left hand corner. Scroll to the right
to see all the columns.
invisible(lapply(sample_sizes, function(sample_size) {
population %>%
sample_n(sample_size) %>%
# select(id, first_name, starts_with("potential_")) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
bind_rows(
summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1),
across(where(is.character), ~ "<<SUMMARY>>"),
id = NA)) %>%
mutate(observed_ate = observed_1 - observed_0) %>%
arrange(desc(row_number())) %>%
select(-id) %>% print()
}))
Try to explain what each column name is referring
to.
Why are there NA values in observed_0 and observed_1
columns?
What is the relationship between potential_0, potential_1,
and unit_tx_effect?
What is the relatinoship between observed_0, observed_1 and
observed_ate?*
What is our actual treatment effect? What do you notice about
the observed ATE as the sample increases?*
Non-compliance
Here we do the same exercise as above but consider the case where we
have non-compliers. Specifically we have 60% compliers, 20% never-takers
and 20% always-takers. We assume defiers do not exist in our population
(not always true). Instead of estimating just the ATE, we’re now
estimating the intent-to-treat effect (ITT) and the Complier Average
Causal Effect (CACE aka LATE) which is the ATE for the compliers.
Scroll to the right to see all the columns.
# (3/5 complier, 1/5 nevertaker, 1/5 alwaystaker)
complier_types <- c("alwaystaker", "alwaystaker",
"nevertaker", "nevertaker",
"complier", "complier", "complier",
"complier", "complier", "complier")
invisible(lapply(sample_sizes, function(sample_size) {
population %>%
# select sample_size units randomly
sample_n(sample_size) %>%
# select client name and potential outcomes
select(first_name, starts_with("potential_")) %>%
# calculate true unit-level treatment effect
mutate(unit_tx_effect = potential_1 - potential_0) %>%
# randomly assign treatment and control (50/50)
arrange(runif(n())) %>%
mutate(group = if_else(row_number() < n() / 2, "treatment", "control")) %>%
arrange(runif(n())) %>%
# randomly assign complier types
# mutate(compliance_group = complier_types[ntile(runif(n()), n = 5)]) %>%
mutate(compliance_group = complier_types[floor(10 * runif(n())) + 1]) %>%
# set observed outcomes based on control and treatment assignment
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
# set observed outcomes based on complier type
mutate(
observed_0 = if_else(
compliance_group == "alwaystaker" & !is.na(observed_0), potential_1,
observed_0),
observed_1 = if_else(
compliance_group == "nevertaker" & !is.na(observed_1), potential_0,
observed_1)) %>%
# complier type shares
mutate(is_taker_tx = if_else(
group == "treatment",
compliance_group %in% c("alwaystaker", "complier"), NA),
is_taker_ctl = if_else(
group == "control",
compliance_group %in% c("alwaystaker"), NA)) %>%
# calculate summaries
bind_rows(
summarise(., across(starts_with(c("observed_", "is_")), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1), first_name = "<<SUMMARY>>")
) %>%
mutate(observed_itt_effect = observed_1 - observed_0) %>%
arrange(desc(row_number())) %>%
mutate(alpha = is_taker_tx - is_taker_ctl,
cace = observed_itt_effect / alpha) %>%
print()
}))
Building from your knowledge from the last exercise, describe
the new columns in these tables: is_taker_tx, is_taker_ctl,
observed_itt_effect, alpha, and cace.
What is the relationship between is_taker_tx, is_taker_ctl,
and alpha?
What is the relationship between obs_itt, obs_alpha, and
obs_cace? (obs = observed)
What do you notice about alpha and cace as the sample size
increases?
Statistical significance
What does statistical significance mean and how does it relate to
sample size? We take the simpler case of Full Compliance from above and
simulate the random assignment of treatment 100 times:
num_simulations <- 100
study_samples <- map(sample_sizes, ~ population %>% sample_n(.x))
simulation_results <- map_dfr(1:num_simulations, function(x) {
map_dfr(study_samples, function(study_sample) {
study_sample %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
mutate(sample_size = n()) %>%
summarise(t_test = list(t.test(observed_1, observed_0)),
across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(observed_ate = observed_1 - observed_0,
p_value = map_dbl(t_test, ~ .x$p.value),
confint_left = map_dbl(t_test, ~ .x$conf.int[[1]]),
confint_right = map_dbl(t_test, ~ .x$conf.int[[2]]))
})
})
simulation_results %>%
ggplot(aes(observed_ate)) +
geom_histogram(binwidth = 100) +
facet_wrap(~ sample_size, scales = "free_y")

# simulation_results %>%
# ggplot(aes(p_value, fill = sample_size)) +
# # geom_density(alpha = 0.1) +
# geom_histogram(binwidth = 0.01) +
# facet_wrap(~ sample_size, scales = "free_y")
#
# simulation_results %>%
# filter(sample_size == 1000000) %>%
# select(p_value, confint_left, observed_ate, confint_right) %>%
# mutate(covered = observed_ate[1] > confint_left & observed_ate[1] < confint_right) %>%
# summarise(mean(covered), p_value = p_value[1])
What do you notice about the distribution of ATEs we get from
the simulations with different sample sizes?
A coding walkthrough
Maybe this all makes sense to you but the coding is still hard to
fully understand. That’s understandable! Coding is like any language, it
takes a long time to learn, practice and understand. Let’s walk through
the code that generated the tables from teh Full Compliance section
above: @ref(tab:full-compliance).
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
bind_rows(
summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1),
across(where(is.character), ~ "<<SUMMARY>>"),
id = NA)) %>%
mutate(observed_ate = observed_1 - observed_0) %>%
arrange(desc(row_number())) %>%
select(-id)
We go through this line by line. First it’s simply the population
data frame we generated at the beginning. A data frame is
simply data arranged as a table of rows and columns.
population
We define a variable called sample_size and give it a
value of 100.
sample_size <- 100
We then randomly sample, in this case, sample_size
number of rows from this data frame:
population %>%
sample_n(sample_size)
Next we mutate the data frame by adding a new column
called unit_tx_effect which is defined as
potential_1 - potential_0 the difference
between the two potential outcomes. The verb mutate simply
means that we’re modifying or adding to the data frame with this
operation.
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0)
You will notice that this new column is created as the last
(right-most) column of this data frame.
Next, we arrange the rows of the data frame by
runif(n()).
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n()))
The runif(n()) function simply generates random chosen
numbers between 0 and 1 for each row in the data frame.
arrange sorts the data frame by this row and we have rows
that are now in random order.
You can play with runif() below to see how it works:
runif(10)
[1] 0.6285034 0.3483613 0.3654089 0.5586069 0.7535734 0.5551276 0.6164481 0.2663728 0.7262539 0.7536303
Why did we order the rows randomly? Well this leads to our next step
in mutate-ing the first half of the rows as treatment, and
the second half of the rows as control. Since they are randomly ordered,
this will create our random assignment of treatment.
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control"))
The
if_else(row_number() <= n() / 2, "treatment", "control")
expression tells us that if the row number is less than or equal to the
total row numbers divided by 2, assign the value treatment
otherwise assign the value control. You will notice a new
column called group created on the rightmost side of this
data frame.
The next arrange simply orders the rows in the original
id order, there’s no special reason to do this except
keeping our data frame in order of id numbers. The next step defines
some more columns in our data frame.
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_))
Similar to the treatment vs control
assignment to the group column, these two new columns
define the observed outcome in the treatment group,
observed_1 and the observed outcome in the control group,
observed_0. The logic should look familiar:
observed_0 = if_else(group == "control", potential_0, NA_real_)
suggests that if the group column has the value control
then we should assign observed_0 the value of the potential
outcome without treatment, potential_0. Otherwise, the
column gets an NA_real_ value which simply means the data
is missing since the individual was in the other treatment
group. The same applies to the creation of the observed_1
column.
The next chunk of code beginning with bind_rows is
fairly complicated and not particularly elegant. I will skip the
detailed explanation for now but the purpose of this step is to create a
summary row that takes the mean of all the numeric columns of the data
and rounds them to the first significant digit. It also replaces columns
that are of a non-numeric character type and replaces it will the value
<<SUMMARY>>.
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
bind_rows(
summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1),
across(where(is.character), ~ "<<SUMMARY>>"),
id = NA))
Let’s do a slight modification of the code by taking out the
bind_rows function to get an intuition of what might be
going on here:
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1),
across(where(is.character), ~ "<<SUMMARY>>"),
id = NA)
We see that we create a single row that averages the values from each
numeric column (ignoring the missing NA values) and roudns it to a
single significant digit. This row is simply added as the first row in
the data frame and then the difference between observed_1
and observed_0 is taken as the
observed_ate:
population %>%
sample_n(sample_size) %>%
mutate(unit_tx_effect = potential_1 - potential_0) %>%
arrange(runif(n())) %>%
mutate(group = if_else(row_number() <= n() / 2, "treatment", "control")) %>%
arrange(id) %>%
mutate(observed_0 = if_else(group == "control", potential_0,
NA_real_),
observed_1 = if_else(group == "treatment", potential_1,
NA_real_)) %>%
bind_rows(
summarise(., across(where(is.numeric), mean, na.rm = TRUE)) %>%
mutate(across(where(is.numeric), round, digits = 1),
across(where(is.character), ~ "<<SUMMARY>>"),
id = NA)) %>%
mutate(observed_ate = observed_1 - observed_0) %>%
arrange(desc(row_number())) %>%
select(-id)
As a finishing touch we reverse the order of the rows so that the
newly added summary row is on top, and then remove the id
column to reduce the number of columns of the data frame displayed.
LS0tCnRpdGxlOiAiUG90ZW50aWFsIE91dGNvbWVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGlzIG5vdGVib29rIGV4cGxhaW5zIHRoZSBbUG90ZW50aWFsIE91dGNvbWVzIEZyYW1ld29ya10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUnViaW5fY2F1c2FsX21vZGVsKSBhbmQgZXN0aW1hdGlvbiBvZiBDYXVzYWwgRWZmZWN0cywgaW4gcGFydGljdWxhciB0aGUgW0F2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQXZlcmFnZV90cmVhdG1lbnRfZWZmZWN0KSBhbmQgcmVsYXRlZCBlc3RpbWF0b3JzOiB0aGUgW0ludGVudC10by1UcmVhdCBFZmZlY3RdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0ludGVudGlvbi10by10cmVhdF9hbmFseXNpcykgYW5kIHRoZSBbQ29tcGxpZXIgQXZlcmFnZSBDYXVzYWwgRWZmZWN0XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Mb2NhbF9hdmVyYWdlX3RyZWF0bWVudF9lZmZlY3QpIChhbHNvIGtub3duIGFzIHRoZSBMb2NhbCBBdmVyYWdlIFRyZWF0bWVudCBFZmZlY3QpLgoKVXNpbmcgYSBnZW5lcmF0ZWQgcG9wdWxhdGlvbiBhcyBhIHJlZmVyZW5jZSwgd2UgZXhhbWluZSB0aGUgaW1wbGljYXRpb25zIG9mIHJhbmRvbSBzYW1wbGluZyBhbmQgcmFuZG9tIGFzc2lnbm1lbnQgb2YgdHJlYXRtZW50LCBub24tY29tcGxpYW5jZSwgYW5kIGNvbnNlcXVlbmNlcyBvZiBsaW1pdGVkIHNhbXBsZSBzaXplcy4KClRoZSBjb2RlIG5lZWRlZCB0byBydW4gdGhpcyBhbmFseXNpcyBjYW4gYmUgZmluZCBpbiB0aGUgZm9sbG93aW5nIEdpdEh1YiBbcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tL3JvYm90b24vcmFuZG9taXplKSB3aGVyZSB5b3UgY2FuIGRvd25sb2FkLCBjb21tZW50LCBvciBtYWtlIGNvbnRyaWJ1dGlvbnMgb2YgeW91ciBvd24uCgojIyBSZXF1aXJlbWVudHMKCkZpcnN0LCB5b3Ugd2lsbCBuZWVkIFJTdHVkaW8gdG8gcnVuIHRoaXMgbm90ZWJvb2suIFBsZWFzZSBkb3dubG9hZCBpdCBbaGVyZV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8jZG93bmxvYWQpIGFuZCB0aGVuIGluc3RhbGwgaXQuCgpUaGVyZSBhcmUgdHdvIGxpYnJhcmllcyBuZWVkZWQgdG8gcnVuIHRoaXMgUiBOb3RlYm9vazoKMS4gW3RpZHl2ZXJzZV0oaHR0cHM6Ly90aWR5dmVyc2UudGlkeXZlcnNlLm9yZy8pIChmb3IgZGF0YSBtYW5pcHVsYXRpb24pCjIuIFtyYW5kb21OYW1lc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JhbmRvbU5hbWVzL3ZpZ25ldHRlcy9yYW5kb21OYW1lcy5odG1sKSAodG8gZ2VuZXJhdGUgdGhlIHBvcHVsYXRpb24pCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21OYW1lcyIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJhbmRvbU5hbWVzKQpgYGAKCllvdSBtYXkgZXhwZXJpZW5jZSBlcnJvcnMgdHJ5aW5nIHRvIGluc3RhbGwgdGhlc2UgcGFja2FnZXMuIFlvdXIgYmVzdCBmcmllbmQsIGluIHRoaXMgY2FzZSwgaXMgR29vZ2xlLiBTZWFyY2ggZm9yIHRoZSBlcnJvciBtZXNzYWdlcyB5b3UgcmVjZWl2ZSBhbmQgdHJ5IHRvIHRyb3VibGVzaG9vdCB5b3VyIHdheSB0aHJvdWdoIGluc3RhbGxpbmcgdGhlIHBhY2thZ2UuIFVuZm9ydHVuYXRlbHkgdGhlcmUgaXNuJ3QgYSBjb25zaXN0ZW50IHNvbHV0aW9uIHRvIHRoZXNlIHByb2JsZW1zIGFzIGRpZmZlcmVudCBvcGVyYXRpbmcgc3lzdGVtcyBhbmQgbG9jYWwgc2V0dGluZ3MgY2FuIGNhdXNlIGEgdmFyaWV0eSBvZiBpc3N1ZXMuIEhvd2V2ZXIsIGFsbCB0aGVzZSBpc3N1ZXMgc2hvdWxkIGJlIGFibGUgdG8gYmUgcmVzb2x2ZWQuCgojIyBHZW5lcmF0aW5nIHVwIG91ciBwb3B1bGF0aW9uCgpMZXRzIGRlZmluZSBhbmQgZ2VuZXJhdGUgdGhlIHBvcHVsYXRpb24gd2UncmUgZ29pbmcgdG8gc3R1ZHkuIEJlbG93IHdlIHNldCBzb21lIGdlbmVyYWwgY2hhcmFjdGVyaXN0aWNzOgotIHRoZSBzaXplIG9mIHRoZSBwb3B1bGF0aW9uLAotIGdlbmRlciBhbmQgZXRobmljaXR5IGRlbW9ncmFwaGljcyBhbmQKLSB0aGUgYmFzZSBsZXZlbCBpbmNvbWUgYW5kIHZhcmlhdGlvbiAoc3RhbmRhcmQgZGV2aWF0aW9uKSBvZiB0aGF0IGluY29tZS4KCmBgYHtyfQpwb3B1bGF0aW9uX3NpemUgPC0gMTAwMDAwMApiYXNlX2luY29tZSA8LSAxMDAwMApiYXNlX2luY29tZV9zZCA8LSAxMDAwCgpldGhuaWNpdGllcyA8LSBmYWN0b3IoYygiSW5kaWFuIiwgIkFzaWFuIiwgIkJsYWNrIiwgIkhpc3BhbmljIiwgIldoaXRlIiwgIkFyYWJpYyIpKQpnZW5kZXJzIDwtIGZhY3RvcihjKCJGZW1hbGUiLCAiTWFsZSIpKQoKYGBgCgoqKkhvdyBtYW55IHBlb3BsZSBoYXZlIHdlIGRlZmluZWQgdG8gYmUgaW4gb3VyIHBvcHVsYXRpb24/KioKCioqV2hhdCBpcyB0aGVpciBleHBlY3RlZCBiYXNlIGluY29tZT8qKgoKKipIb3cgbWFueSBkaWZmZXJlbnQgZXRobmljaXRpZXMgYXJlIGluIG91ciBwb3B1bGF0aW9uPyoqCgpgYGB7cn0KZGVtb2dyYXBoaWNzIDwtIHRpYmJsZSgKICBnZW5kZXIgPSBzYW1wbGUoZ2VuZGVycywgcG9wdWxhdGlvbl9zaXplLCBwcm9iID0gcnVuaWYobGVuZ3RoKGdlbmRlcnMpKSwKICAgICAgICAgICAgICAgICAgcmVwbGFjZSA9IFRSVUUpLAogIGV0aG5pY2l0eSA9IHNhbXBsZShldGhuaWNpdGllcywgcG9wdWxhdGlvbl9zaXplLCBwcm9iID0gcnVuaWYobGVuZ3RoKGV0aG5pY2l0aWVzKSksCiAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2UgPSBUUlVFKSkKCmluY29tZV9tYWxlX2ZhY3RvciA8LSBydW5pZigxKQppbmNvbWVfZXRobmljaXR5X2ZhY3RvciA8LSBydW5pZigxKQoKcG9wdWxhdGlvbiA8LSByYW5kb21OYW1lcyhwb3B1bGF0aW9uX3NpemUsIHJldHVybi5jb21wbGV0ZS5kYXRhID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5kZXIgPSBhcy5udW1lcmljKGRlbW9ncmFwaGljcyRnZW5kZXIpIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICBldGhuaWNpdHkgPSBhcy5udW1lcmljKGRlbW9ncmFwaGljcyRldGhuaWNpdHkpKSAlPiUKICBtdXRhdGUoaWQgPSBmYWN0b3Iocm93X251bWJlcigpKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5mYWN0b3IpKSAlPiUKICBtdXRhdGUoaW5jb21lID0gcm91bmQocm5vcm0ocG9wdWxhdGlvbl9zaXplLCBiYXNlX2luY29tZSwgYmFzZV9pbmNvbWVfc2QpKSkgJT4lCiAgbXV0YXRlKGluY29tZSA9IGluY29tZSArIGluY29tZSAqIChnZW5kZXIgKiBpbmNvbWVfbWFsZV9mYWN0b3IpKSAlPiUKICBtdXRhdGUoaW5jb21lID0gaW5jb21lICsgaW5jb21lICogKGV0aG5pY2l0eSAqIGluY29tZV9ldGhuaWNpdHlfZmFjdG9yKSkgJT4lCiAgbXV0YXRlKGdlbmRlciA9IGZhY3RvcihtYXBfY2hyKGdlbmRlciwgfiBsZXZlbHMoZ2VuZGVycylbLnggKyAxXSkpLAogICAgICAgICBldGhuaWNpdHkgPSBmYWN0b3IobWFwX2NocihldGhuaWNpdHksIH4gbGV2ZWxzKGV0aG5pY2l0aWVzKVsueF0pKSkKCnN1bW1hcnkocG9wdWxhdGlvbikKCnBvcHVsYXRpb24gJT4lCiAgZ2dwbG90KGFlcyhldGhuaWNpdHksIGZpbGwgPSBldGhuaWNpdHkpKSArCiAgZ2VvbV9iYXIoKQoKcG9wdWxhdGlvbiAlPiUKICBnZ3Bsb3QoYWVzKGdlbmRlciwgZmlsbCA9IGdlbmRlcikpICsKICBnZW9tX2JhcigpCgpwb3B1bGF0aW9uICU+JQogIGdncGxvdChhZXMoaW5jb21lLCBmaWxsID0gZXRobmljaXR5LCBsdHkgPSBnZW5kZXIpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArCiAgZmFjZXRfd3JhcCh+IGV0aG5pY2l0eSwgbmNvbCA9IDEpCmBgYAoKKipIb3cgbXVjaCBtb3JlL2xlc3MgaW5jb21lIGRvIG1hbGVzIG1ha2UgY29tcGFyZWQgdG8gZmVtYWxlcz8qKgoKKipPbiBhdmVyYWdlLCB3aGljaCBldGhuaWNpdHkgbWFrZXMgdGhlIG1vc3Q/KioKCioqT24gYXZlcmFnZSwgd2hpY2ggZXRobmljaXR5IG1ha2VzIHRoZSBsZWFzdD8qKgoKKipBcmUgdGhlcmUgYW55IHNpZ25pZmljYW50IGRpZmZlcmVuY2VzIGluIHRoZSBwcm9wb3J0aW9uIG9mIGdlbmRlcnM/KioKCioqQXJlIHRoZXJlIGFueSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBpbiB0aGUgcHJvcG9ydGlvbiBvZiBldGhuaWNpdGllcz8qKgoKIyMgRGVmaW5pbmcgcG9wdWxhdGlvbiBwb3RlbnRpYWwgb3V0Y29tZXMgKEdvZC1tb2RlKQoKTm93IHRoYXQgd2UgaGF2ZSBhIHBvcHVsYXRpb24sIHdlIHdpbGwgZ2VuZXJhdGUgdGhlIGVmZmVjdHMgdGhhdCB0aGUgcG9saWN5IF93aWxsXyBoYXZlIG9uIGVhY2ggaW5kaXZpZHVhbC4gU2luY2Ugd2UgaGF2ZSBmdWxsIGtub3dsZWRnZSBhbmQgY29udHJvbCBvZiB0aGlzIHBvcHVsYXRpb24sIHdlIHdpbGwga25vdyB0aGUgdHJlYXRtZW50IGVmZmVjdCBmb3IgZWFjaCBpbmRpdmlkdWFsIGJ5IGRlZmluaW5nIGJvdGggb2YgdGhlaXIgcG90ZW50aWFsIG91dGNvbWVzICh3aXRoIGFuZCB3aXRob3V0IHRyZWF0bWVudCkuIFRoZSByZXNlYXJjaGVyIHdpbGwgX25vdF8ga25vdyB0aGVzZSB2YWx1ZXMsIGl0IGlzIHRoZWlyIGpvYiB0byByZWNvdmVyIHRoZXNlIHZhbHVlcy4KCmBgYHtyfQplZmZlY3Rfc2l6ZSA8LSAzMDAwCmVmZmVjdF9zZCA8LSAxMDAwCmluY29tZV90aW1lX2ZhY3RvciA8LSBydW5pZigxKQoKcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9uICU+JQogIG11dGF0ZShwb3RlbnRpYWxfMCA9IGluY29tZSAqIGluY29tZV90aW1lX2ZhY3RvcikgJT4lCiAgbXV0YXRlKHBvdGVudGlhbF8xID0gcm91bmQoCiAgICBwb3RlbnRpYWxfMCArIHJub3JtKHBvcHVsYXRpb25fc2l6ZSwgZWZmZWN0X3NpemUsIGVmZmVjdF9zZCkpKQoKcG9wdWxhdGlvbiAlPiUKICBzdW1tYXJpc2UoYWNyb3NzKGMoaW5jb21lLCBwb3RlbnRpYWxfMCwgcG90ZW50aWFsXzEpLCBsaXN0KGBfbWVhbmAgPSBtZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYF9zdGRkZXZgID0gc2QpKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfc2VwID0gIl9fIiwgbmFtZXNfdG8gPSBjKCJtZXRyaWMiLCAiYWdnIikpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBhZ2cpCgpwb3B1bGF0aW9uICU+JQogIHBpdm90X2xvbmdlcihjKGluY29tZSwgcG90ZW50aWFsXzAsIHBvdGVudGlhbF8xKSkgJT4lCiAgZ2dwbG90KGFlcyh2YWx1ZSwgZmlsbCA9IG5hbWUpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC4yKSArCiAgZmFjZXRfZ3JpZChldGhuaWNpdHkgfiBnZW5kZXIsIHNjYWxlcyA9ICJmcmVlIikKYGBgCgoqKldoYXQgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIHdpdGhvdXQgdHJlYXRtZW50PyoqCgoqKldoYXQgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWVzIHdpdGggdHJlYXRtZW50PyoqCgoqKldoYXQgaXMgb3VyIGFjdHVhbCBwb3B1bGF0aW9uIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdD8qKgoKKipXaGF0IHdvdWxkIGhhdmUgaGFwcGVuZWQgdG8gaW5jb21lcyBpZiBldmVyeW9uZSByZWNlaXZlZCB0cmVhdG1lbnQ/KioKCioqV2hhdCB3b3VsZCBoYXZlIGhhcHBlbmVkIHRvIGluY29tZXMgaWYgbm8gb25lIHJlY2VpdmVkIHRyZWF0bWVudD8qKgoKIyMgU2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiAocmVzZWFyY2hlciBtb2RlKQoKQXMgYSByZXNlYXJjaGVyLCB3ZSB1c3VhbGx5IGRvbid0IGhhdmUgYWNjZXNzIHRvIHRoZSBlbnRpcmUgcG9wdWxhdGlvbiBnaXZlbiBhIGxpbWl0ZWQgcmVzZWFyY2ggYnVkZ2V0LiBXZSB0eXBpY2FsbHkgaGF2ZSB0byBjaG9vc2UgYSBzdWJzZXQgb2YgdGhpcyBwb3B1bGF0aW9uLiBJZGVhbGx5LCB3ZSBzaG91bGQgcmFuZG9tbHkgc2FtcGxlIGZyb20gdGhlIGdyb3VwIG9mIHBlb3BsZSBmcm9tIHRoZSBwb3B1bGF0aW9uIHRoYXQgd2Ugd291bGQgbGlrZSB0byBzdHVkeSBpbiBvdXIgcmVzZWFyY2guCgpJZiB0aGUgcG9saWN5IG9yIHJlc2VhcmNoIHF1ZXN0aW9uIHVuZGVyIGNvbnNpZGVyYXRpb24gYXBwbGllcyB0byBldmVyeW9uZSwgd2UnbGwgd2FudCB0byByYW5kb21seSBzYW1wbGUgZnJvbSB0aGUgZW50aXJlIHBvcHVsYXRpb24uIEl0IGNvdWxkIGFsc28gYmUgdGhhdCB0aGUgcG9saWN5IG9ubHkgYXBwbGllcyB0byB3b21lbiwgb3IgdG8gY2VydGFpbiBldGhuaWNpdGllcywgaW4gd2hpY2ggY2FzZSB0aGVyZSdzIG5vIG5lZWQgdG8gc2FtcGxlIGZyb20gdGhlIGVudGlyZSBwb3B1bGF0aW9uLCBvbmx5IHRoZSBzdWItcG9wdWxhdGlvbiAoZS5nLiB3b21lbiBvciBhIHNwZWNpZmljIGV0aG5pY2l0eSkgb2YgY29uY2Vybi4KCmBgYHtyfQpzYW1wbGVfc2l6ZXMgPC0gMTAgXiBzZXEoMSwgbG9nKHBvcHVsYXRpb25fc2l6ZSwgMTApKQoKcG9wdWxhdGlvbl9zYW1wbGVzIDwtIG1hcF9kZnIoc2FtcGxlX3NpemVzLCBmdW5jdGlvbihzYW1wbGVfc2l6ZSkgewogIHBvcHVsYXRpb24gJT4lCiAgICBzYW1wbGVfbihzYW1wbGVfc2l6ZSkgJT4lCiAgICBtdXRhdGUoc2FtcGxlX3NpemUgPSBzYW1wbGVfc2l6ZSkKfSkKCnBvcHVsYXRpb25fc2FtcGxlcyAlPiUKICBnZ3Bsb3QoYWVzKGdlbmRlciwgZmlsbCA9IGdlbmRlcikpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKH4gc2FtcGxlX3NpemUsIHNjYWxlcyA9ICJmcmVlX3giKSArCiAgY29vcmRfZmxpcCgpCgpwb3B1bGF0aW9uX3NhbXBsZXMgJT4lCiAgZ2dwbG90KGFlcyhldGhuaWNpdHksIGZpbGwgPSBldGhuaWNpdHkpKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh+IHNhbXBsZV9zaXplLCBzY2FsZXMgPSAiZnJlZV94IikgKwogIGNvb3JkX2ZsaXAoKQoKcG9wdWxhdGlvbl9zYW1wbGVzICU+JQogIGdncGxvdChhZXMoaW5jb21lKSkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwMCkgKwogIGZhY2V0X3dyYXAofiBzYW1wbGVfc2l6ZSwgc2NhbGVzID0gImZyZWUiKQpgYGAKCioqV2hhdCBhcmUgdGhlIGRpZmZlcmVudCBzYW1wbGUgc2l6ZXMgd2Ugd2lsbCB3b3JrIHdpdGggYXMgYSByZXNlYXJjaGVyPyoqCgoqKldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIG9mIGdlbmRlcnMsIGV0aG5pY2l0aWVzLCBhbmQgaW5jb21lcyBhcyBzYW1wbGUgc2l6ZSBpbmNyZWFzZXM/KioKCioqSG93IGRvZXMgcmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBoZWxwIGluIGVzdGltYXRpbmcgdGhlIHRyZWF0bWVudCBlZmZlY3Qgb2Ygb3VyIHByb2dyYW0/KioKCiMjIEVzdGltYXRpbmcgdGhlIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCAocmVzZWFyY2hlciBtb2RlKQoKUmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBoZWxwcyBlbnN1cmUgb3VyIHN0dWR5IHNhbXBsZSBpcyByZXByZXNlbnRhdGl2ZSBvZiB0aGUgcG9wdWxhdGlvbiBhdCBsYXJnZS4gSG93ZXZlciwgcmFuZG9tbHkgc2FtcGxpbmcgZnJvbSB0aGUgcG9wdWxhdGlvbiBkb2VzIF9ub3RfIGhlbHAgdXMgZXN0aW1hdGUgdGhlIGVmZmVjdCBvZiBvdXIgcG9saWN5LiBJbiBvcmRlciB0byBlc3RpbWF0ZSB0aGUgZWZmZWN0IG9mIG91ciBwb2xpY3kgd2UgbmVlZCB0byBjb21wYXJlIF9Qb3RlbnRpYWwgT3V0Y29tZXNfLCBzcGVjaWZpY2FsbHkgdGhlIHBvdGVudGlhbCBvdXRjb21lIHdoZW4gc29tZW9uZSBnZXRzIHRyZWF0bWVudCBhbmQgdGhlIG91dGNvbWUgd2hlbiB0aGV5IGRvIG5vdCBnZXQgdHJlYXRtZW50PyBUaGUgYXZlcmFnZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHR3byBwb3RlbnRpYWwgb3V0Y29tZXMgaXMga25vd24gYXMgdGhlIEF2ZXJhZ2UgVHJlYXRtZW50IEVmZmVjdCAoQVRFKS4KCioqRG9lcyB0aGUgcmVzZWFyY2hlciBrbm93IHRoZSB2YWx1ZXMgb2YgYm90aCBwb3RlbnRpYWwgb3V0Y29tZXMgZm9yIHRoZSBpbmRpdmlkdWFscyBpbiB0aGVpciBzYW1wbGU/KioKCioqRGVzY3JpYmUsIGluIGEgZmV3IHNlbnRlbmNlcywgaG93IHlvdSBjb3VsZCBlc3RpbWF0ZSB0aGUgQVRFIGZyb20gdGhlIHNhbXBsZXMgd2UgYXJlIGdpdmVuPyoqCgojIyMgRnVsbCBjb21wbGlhbmNlCgpJbiBvcmRlciB0byBiZSBhYmxlIHRvIGVzdGltYXRlIHRoZSBBVEUsIHdlIG5lZWQgdG8gY29uc3RydWN0IHR3byBzdGF0aXN0aWNhbGx5IGlkZW50aWNhbCBncm91cHMgd2l0aCB0aGUgZXhjZXB0aW9uIHRoYXQgb25lIG9mIHRoZXNlIGdyb3VwcyByZWNlaXZlcyB0cmVhdG1lbnQgd2hpbGUgdGhlIG90aGVycyBkbyBub3QuIFRoaXMgaXMgdGhlIGNvcmUgb2YgdGhlIFtSYW5kb21pemVkIENvbnRyb2xsZWQgVHJpYWxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1JhbmRvbWl6ZWRfY29udHJvbGxlZF90cmlhbCkgKFJDVCkuCgpDZXJ0YWluIHNldHRpbmdzIGZvciBSQ1RzIGVuc3VyZSB0aGF0IGV2ZXJ5b25lIHdobyBpcyBpbiB0aGUgdHJlYXRtZW50IGdyb3VwIGdldHMgdHJlYXRtZW50IHdoaWxlIG5vIG9uZSBpbiB0aGUgY29udHJvbCBncm91cCByZWNlaXZlcyB0cmVhdG1lbnQuIFRoaXMgaXMgbW9yZSBkaWZmaWN1bHQgdG8gZW5zdXJlIGluIG1vc3QgY2FzZXMgaW52b2x2aW5nIGh1bWFuIHN1YmplY3RzIHNpbmNlIGh1bWFuIHN1YmplY3RzIGhhdmUgdGhlIGZyZWVkb20gdG8gY2hvb3NlIHRvIHRha2UgdXAgdGhlIHRyZWF0bWVudCBvciBub3QuIFRoZSBhYmlsaXR5IGZvciB0aG9zZSBpbiB0aGUgdHJlYXRtZW50IGdyb3VwIHRvIGRlbnkgdHJlYXRtZW50IGFuZCB0aG9zZSBpbiB0aGUgY29udHJvbCBncm91cCB0byB0YWtlIHVwIHRyZWF0bWVudCBpcyBrbm93biBhcyBub24tY29tcGxpYW5jZSBhbmQgdGhlIHVuZGVybHlpbmcgYXNzdW1wdGlvbiBvZiB3aGljaCB0eXBlcyBvZiB0aGlzIG5vbi1jb21wbGlhbmNlIGV4aXN0cyBuZWVkcyB0byBiZSBldmF1bGF0ZWQgb24gYSBjYXNlLWJ5LWNhc2UgYmFzaXMuCgoqKldoYXQgYXJlIHRoZSBmb3VyIGRpZmZlcmVudCB0eXBlcyBvZiBpbmRpdmlkdWFscyBpbiBvdXIgc2FtcGxlIGFuZCB3aGljaCBvZiB0aG9zZSBpbmRpdmlkdWFscyBhcmUgYXNzdW1lZCB0byBleGlzdCBhbmQgbm90IGV4aXN0IHVuZGVyIGZ1bGwgY29tcGxpYW5jZT8qKgoKRmlyc3Qgd2Ugc2ltdWxhdGUgdGhlIFJDVCB3aXRoIGVhY2ggb2YgdGhlIHNhbXBsZSBzaXplcyBmcm9tIGFib3ZlIHdpdGggX0Z1bGwgQ29tcGxpYW5jZV8uCgpUaGUgY29kZSBiZWxvdyBzaW11bGF0ZXMgcmFuZG9tIGFzc2lnbm1lbnQgb2YgdHJlYXRtZW50IHRvIGhhbGYgb2YgdGhlIHN0dWR5IHNhbXBsZSBmb3IgaW5jcmVhc2luZyBzdHVkeSBzYW1wbGUgc2l6ZXMgYW5kIHRyaWVzIHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGJhc2VkIG9uIHdoYXQgaXMgb2JzZXJ2ZWQgZHVlIHRvIHRyZWF0bWVudCBhc3NpZ25tZW50LgoKVGhlIGZpcnN0IHJvdyBpcyBhIHN1bW1hcnkgcm93IHRha2luZyB0aGUgYXZlcmFnZSBvZiBhbGwgb2YgdGhlIGluZGl2aWR1YWwgcm93cyBiZWxvdyBpdC4gVGhlIG9ubHkgdGhpbmcgdGhhdCBjaGFuZ2VzIGZyb20gdGFibGUgdG8gdGFibGUgaXMgdGhlIG51bWJlciBvZiBpbmRpdmlkdWFscyBpbiB0aGF0IHNhbXBsZSwgbm90aWNlIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIHRhYmxlIGluIHRoZSBib3R0b20gbGVmdCBoYW5kIGNvcm5lci4gU2Nyb2xsIHRvIHRoZSByaWdodCB0byBzZWUgYWxsIHRoZSBjb2x1bW5zLgoKYGBge3IgZnVsbC1jb21wbGlhbmNlfQppbnZpc2libGUobGFwcGx5KHNhbXBsZV9zaXplcywgZnVuY3Rpb24oc2FtcGxlX3NpemUpIHsKICBwb3B1bGF0aW9uICU+JQogICAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogICAgIyBzZWxlY3QoaWQsIGZpcnN0X25hbWUsIHN0YXJ0c193aXRoKCJwb3RlbnRpYWxfIikpICU+JQogICAgbXV0YXRlKHVuaXRfdHhfZWZmZWN0ID0gcG90ZW50aWFsXzEgLSBwb3RlbnRpYWxfMCkgJT4lCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogICAgYXJyYW5nZShpZCkgJT4lCiAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZShncm91cCA9PSAidHJlYXRtZW50IiwgcG90ZW50aWFsXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgYmluZF9yb3dzKAogICAgICBzdW1tYXJpc2UoLiwgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCB+ICI8PFNVTU1BUlk+PiIpLAogICAgICAgICAgICAgICBpZCA9IE5BKSkgJT4lCiAgICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogICAgYXJyYW5nZShkZXNjKHJvd19udW1iZXIoKSkpICU+JQogIHNlbGVjdCgtaWQpICU+JSBwcmludCgpCn0pKQpgYGAKCioqVHJ5IHRvIGV4cGxhaW4gd2hhdCBlYWNoIGNvbHVtbiBuYW1lIGlzIHJlZmVycmluZyB0by4qKgoKKipXaHkgYXJlIHRoZXJlIE5BIHZhbHVlcyBpbiBvYnNlcnZlZF8wIGFuZCBvYnNlcnZlZF8xIGNvbHVtbnM/KioKCioqV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gcG90ZW50aWFsXzAsIHBvdGVudGlhbF8xLCBhbmQgdW5pdF90eF9lZmZlY3Q/KioKCioqV2hhdCBpcyB0aGUgcmVsYXRpbm9zaGlwIGJldHdlZW4gb2JzZXJ2ZWRfMCwgb2JzZXJ2ZWRfMSBhbmQgb2JzZXJ2ZWRfYXRlPyoqKgoKKipXaGF0IGlzIG91ciBhY3R1YWwgdHJlYXRtZW50IGVmZmVjdD8gV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IHRoZSBvYnNlcnZlZCBBVEUgYXMgdGhlIHNhbXBsZSBpbmNyZWFzZXM/KioqCgojIyMgTm9uLWNvbXBsaWFuY2UgCgpIZXJlIHdlIGRvIHRoZSBzYW1lIGV4ZXJjaXNlIGFzIGFib3ZlIGJ1dCBjb25zaWRlciB0aGUgY2FzZSB3aGVyZSB3ZSBoYXZlIG5vbi1jb21wbGllcnMuIFNwZWNpZmljYWxseSB3ZSBoYXZlIDYwJSBjb21wbGllcnMsIDIwJSBuZXZlci10YWtlcnMgYW5kIDIwJSBhbHdheXMtdGFrZXJzLiBXZSBhc3N1bWUgZGVmaWVycyBkbyBub3QgZXhpc3QgaW4gb3VyIHBvcHVsYXRpb24gKG5vdCBhbHdheXMgdHJ1ZSkuIEluc3RlYWQgb2YgZXN0aW1hdGluZyBqdXN0IHRoZSBBVEUsIHdlJ3JlIG5vdyBlc3RpbWF0aW5nIHRoZSBpbnRlbnQtdG8tdHJlYXQgZWZmZWN0IChJVFQpIGFuZCB0aGUgQ29tcGxpZXIgQXZlcmFnZSBDYXVzYWwgRWZmZWN0IChDQUNFIGFrYSBMQVRFKSB3aGljaCBpcyB0aGUgQVRFIGZvciB0aGUgY29tcGxpZXJzLgoKU2Nyb2xsIHRvIHRoZSByaWdodCB0byBzZWUgYWxsIHRoZSBjb2x1bW5zLgoKYGBge3J9CiMgICgzLzUgY29tcGxpZXIsIDEvNSBuZXZlcnRha2VyLCAxLzUgYWx3YXlzdGFrZXIpCmNvbXBsaWVyX3R5cGVzIDwtIGMoImFsd2F5c3Rha2VyIiwgImFsd2F5c3Rha2VyIiwKICAgICAgICAgICAgICAgICAgICAibmV2ZXJ0YWtlciIsICJuZXZlcnRha2VyIiwKICAgICAgICAgICAgICAgICAgICAiY29tcGxpZXIiLCAiY29tcGxpZXIiLCAiY29tcGxpZXIiLAogICAgICAgICAgICAgICAgICAgICJjb21wbGllciIsICJjb21wbGllciIsICJjb21wbGllciIpCgppbnZpc2libGUobGFwcGx5KHNhbXBsZV9zaXplcywgZnVuY3Rpb24oc2FtcGxlX3NpemUpIHsKICBwb3B1bGF0aW9uICU+JQogICAgIyBzZWxlY3Qgc2FtcGxlX3NpemUgdW5pdHMgcmFuZG9tbHkKICAgIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICAgICMgc2VsZWN0IGNsaWVudCBuYW1lIGFuZCBwb3RlbnRpYWwgb3V0Y29tZXMKICAgIHNlbGVjdChmaXJzdF9uYW1lLCBzdGFydHNfd2l0aCgicG90ZW50aWFsXyIpKSAlPiUKICAgICMgY2FsY3VsYXRlIHRydWUgdW5pdC1sZXZlbCB0cmVhdG1lbnQgZWZmZWN0CiAgICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICAgICMgcmFuZG9tbHkgYXNzaWduIHRyZWF0bWVudCBhbmQgY29udHJvbCAoNTAvNTApCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPCBuKCkgLyAyLCAidHJlYXRtZW50IiwgImNvbnRyb2wiKSkgJT4lCiAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgIyByYW5kb21seSBhc3NpZ24gY29tcGxpZXIgdHlwZXMKICAgICMgbXV0YXRlKGNvbXBsaWFuY2VfZ3JvdXAgPSBjb21wbGllcl90eXBlc1tudGlsZShydW5pZihuKCkpLCBuID0gNSldKSAlPiUKICAgIG11dGF0ZShjb21wbGlhbmNlX2dyb3VwID0gY29tcGxpZXJfdHlwZXNbZmxvb3IoMTAgKiBydW5pZihuKCkpKSArIDFdKSAlPiUKICAgICMgc2V0IG9ic2VydmVkIG91dGNvbWVzIGJhc2VkIG9uIGNvbnRyb2wgYW5kIHRyZWF0bWVudCBhc3NpZ25tZW50CiAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZShncm91cCA9PSAidHJlYXRtZW50IiwgcG90ZW50aWFsXzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgIyBzZXQgb2JzZXJ2ZWQgb3V0Y29tZXMgYmFzZWQgb24gY29tcGxpZXIgdHlwZQogICAgbXV0YXRlKAogICAgICBvYnNlcnZlZF8wID0gaWZfZWxzZSgKICAgICAgICBjb21wbGlhbmNlX2dyb3VwID09ICJhbHdheXN0YWtlciIgJiAhaXMubmEob2JzZXJ2ZWRfMCksIHBvdGVudGlhbF8xLAogICAgICAgIG9ic2VydmVkXzApLAogICAgICBvYnNlcnZlZF8xID0gaWZfZWxzZSgKICAgICAgICBjb21wbGlhbmNlX2dyb3VwID09ICJuZXZlcnRha2VyIiAmICFpcy5uYShvYnNlcnZlZF8xKSwgcG90ZW50aWFsXzAsCiAgICAgICAgb2JzZXJ2ZWRfMSkpICU+JQogICAgIyBjb21wbGllciB0eXBlIHNoYXJlcwogICAgbXV0YXRlKGlzX3Rha2VyX3R4ID0gaWZfZWxzZSgKICAgICAgICAgICAgIGdyb3VwID09ICJ0cmVhdG1lbnQiLAogICAgICAgICAgICAgY29tcGxpYW5jZV9ncm91cCAlaW4lIGMoImFsd2F5c3Rha2VyIiwgImNvbXBsaWVyIiksIE5BKSwKICAgICAgICAgICBpc190YWtlcl9jdGwgPSBpZl9lbHNlKAogICAgICAgICAgICAgZ3JvdXAgPT0gImNvbnRyb2wiLAogICAgICAgICAgICAgY29tcGxpYW5jZV9ncm91cCAlaW4lIGMoImFsd2F5c3Rha2VyIiksIE5BKSkgJT4lCiAgICAjIGNhbGN1bGF0ZSBzdW1tYXJpZXMKICAgIGJpbmRfcm93cygKICAgICAgc3VtbWFyaXNlKC4sIGFjcm9zcyhzdGFydHNfd2l0aChjKCJvYnNlcnZlZF8iLCAiaXNfIikpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksIGZpcnN0X25hbWUgPSAiPDxTVU1NQVJZPj4iKQogICAgKSAlPiUKICAgIG11dGF0ZShvYnNlcnZlZF9pdHRfZWZmZWN0ID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogICAgYXJyYW5nZShkZXNjKHJvd19udW1iZXIoKSkpICU+JQogICAgbXV0YXRlKGFscGhhID0gaXNfdGFrZXJfdHggLSBpc190YWtlcl9jdGwsCiAgICAgICAgICAgY2FjZSA9IG9ic2VydmVkX2l0dF9lZmZlY3QgLyBhbHBoYSkgJT4lCiAgICBwcmludCgpCn0pKQpgYGAKCioqQnVpbGRpbmcgZnJvbSB5b3VyIGtub3dsZWRnZSBmcm9tIHRoZSBsYXN0IGV4ZXJjaXNlLCBkZXNjcmliZSB0aGUgbmV3IGNvbHVtbnMgaW4gdGhlc2UgdGFibGVzOiBpc190YWtlcl90eCwgaXNfdGFrZXJfY3RsLCBvYnNlcnZlZF9pdHRfZWZmZWN0LCBhbHBoYSwgYW5kIGNhY2UuKioKCioqV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaXNfdGFrZXJfdHgsIGlzX3Rha2VyX2N0bCwgYW5kIGFscGhhPyoqCgoqKldoYXQgaXMgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG9ic19pdHQsIG9ic19hbHBoYSwgYW5kIG9ic19jYWNlPyAob2JzID0gb2JzZXJ2ZWQpKioKCioqV2hhdCBkbyB5b3Ugbm90aWNlIGFib3V0IGFscGhhIGFuZCBjYWNlIGFzIHRoZSBzYW1wbGUgc2l6ZSBpbmNyZWFzZXM/KioKCiMjIFN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZQoKV2hhdCBkb2VzIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBtZWFuIGFuZCBob3cgZG9lcyBpdCByZWxhdGUgdG8gc2FtcGxlIHNpemU/IFdlIHRha2UgdGhlIHNpbXBsZXIgY2FzZSBvZiBGdWxsIENvbXBsaWFuY2UgZnJvbSBhYm92ZSBhbmQgc2ltdWxhdGUgdGhlIHJhbmRvbSBhc3NpZ25tZW50IG9mIHRyZWF0bWVudCAxMDAgdGltZXM6CgpgYGB7cn0KbnVtX3NpbXVsYXRpb25zIDwtIDEwMApzdHVkeV9zYW1wbGVzIDwtIG1hcChzYW1wbGVfc2l6ZXMsIH4gcG9wdWxhdGlvbiAlPiUgc2FtcGxlX24oLngpKQoKc2ltdWxhdGlvbl9yZXN1bHRzIDwtIG1hcF9kZnIoMTpudW1fc2ltdWxhdGlvbnMsIGZ1bmN0aW9uKHgpIHsKICBtYXBfZGZyKHN0dWR5X3NhbXBsZXMsIGZ1bmN0aW9uKHN0dWR5X3NhbXBsZSkgewogICAgc3R1ZHlfc2FtcGxlICU+JQogICAgICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogICAgICBtdXRhdGUoZ3JvdXAgPSBpZl9lbHNlKHJvd19udW1iZXIoKSA8PSBuKCkgLyAyLCAidHJlYXRtZW50IiwgImNvbnRyb2wiKSkgJT4lCiAgICAgIGFycmFuZ2UoaWQpICU+JQogICAgICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkFfcmVhbF8pLAogICAgICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogICAgICBtdXRhdGUoc2FtcGxlX3NpemUgPSBuKCkpICU+JQogICAgICBzdW1tYXJpc2UodF90ZXN0ID0gbGlzdCh0LnRlc3Qob2JzZXJ2ZWRfMSwgb2JzZXJ2ZWRfMCkpLAogICAgICAgICAgICAgICAgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBtZWFuLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgbXV0YXRlKG9ic2VydmVkX2F0ZSA9IG9ic2VydmVkXzEgLSBvYnNlcnZlZF8wLAogICAgICAgICAgICAgcF92YWx1ZSA9IG1hcF9kYmwodF90ZXN0LCB+IC54JHAudmFsdWUpLAogICAgICAgICAgICAgY29uZmludF9sZWZ0ID0gbWFwX2RibCh0X3Rlc3QsIH4gLngkY29uZi5pbnRbWzFdXSksCiAgICAgICAgICAgICBjb25maW50X3JpZ2h0ID0gbWFwX2RibCh0X3Rlc3QsIH4gLngkY29uZi5pbnRbWzJdXSkpCiAgfSkKfSkKCnNpbXVsYXRpb25fcmVzdWx0cyAlPiUKICBnZ3Bsb3QoYWVzKG9ic2VydmVkX2F0ZSkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEwMCkgKwogIGZhY2V0X3dyYXAofiBzYW1wbGVfc2l6ZSwgc2NhbGVzID0gImZyZWVfeSIpCgojIHNpbXVsYXRpb25fcmVzdWx0cyAlPiUKIyAgIGdncGxvdChhZXMocF92YWx1ZSwgZmlsbCA9IHNhbXBsZV9zaXplKSkgKwojICAgIyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjEpICsKIyAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wMSkgKwojICAgZmFjZXRfd3JhcCh+IHNhbXBsZV9zaXplLCBzY2FsZXMgPSAiZnJlZV95IikKIyAKIyBzaW11bGF0aW9uX3Jlc3VsdHMgJT4lCiMgICBmaWx0ZXIoc2FtcGxlX3NpemUgPT0gMTAwMDAwMCkgJT4lCiMgICBzZWxlY3QocF92YWx1ZSwgY29uZmludF9sZWZ0LCBvYnNlcnZlZF9hdGUsIGNvbmZpbnRfcmlnaHQpICU+JQojICAgbXV0YXRlKGNvdmVyZWQgPSBvYnNlcnZlZF9hdGVbMV0gPiBjb25maW50X2xlZnQgJiBvYnNlcnZlZF9hdGVbMV0gPCBjb25maW50X3JpZ2h0KSAlPiUKIyAgIHN1bW1hcmlzZShtZWFuKGNvdmVyZWQpLCBwX3ZhbHVlID0gcF92YWx1ZVsxXSkKYGBgCgoqKldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGUgZGlzdHJpYnV0aW9uIG9mIEFURXMgd2UgZ2V0IGZyb20gdGhlIHNpbXVsYXRpb25zIHdpdGggZGlmZmVyZW50IHNhbXBsZSBzaXplcz8qKgoKIyMgQSBjb2Rpbmcgd2Fsa3Rocm91Z2gKCk1heWJlIHRoaXMgYWxsIG1ha2VzIHNlbnNlIHRvIHlvdSBidXQgdGhlIGNvZGluZyBpcyBzdGlsbCBoYXJkIHRvIGZ1bGx5IHVuZGVyc3RhbmQuIFRoYXQncyB1bmRlcnN0YW5kYWJsZSEgQ29kaW5nIGlzIGxpa2UgYW55IGxhbmd1YWdlLCBpdCB0YWtlcyBhIGxvbmcgdGltZSB0byBsZWFybiwgcHJhY3RpY2UgYW5kIHVuZGVyc3RhbmQuIExldCdzIHdhbGsgdGhyb3VnaCB0aGUgY29kZSB0aGF0IGdlbmVyYXRlZCB0aGUgdGFibGVzIGZyb20gdGVoIEZ1bGwgQ29tcGxpYW5jZSBzZWN0aW9uIGFib3ZlOiBcQHJlZih0YWI6ZnVsbC1jb21wbGlhbmNlKS4KCmBgYApwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKSAlPiUKICBhcnJhbmdlKGlkKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXyksCiAgICAgICAgIG9ic2VydmVkXzEgPSBpZl9lbHNlKGdyb3VwID09ICJ0cmVhdG1lbnQiLCBwb3RlbnRpYWxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogIGJpbmRfcm93cygKICAgIHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JQogICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgICAgIGlkID0gTkEpKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogIGFycmFuZ2UoZGVzYyhyb3dfbnVtYmVyKCkpKSAlPiUKICBzZWxlY3QoLWlkKQpgYGAKCldlIGdvIHRocm91Z2ggdGhpcyBsaW5lIGJ5IGxpbmUuIEZpcnN0IGl0J3Mgc2ltcGx5IHRoZSBwb3B1bGF0aW9uIGRhdGEgZnJhbWUgd2UgZ2VuZXJhdGVkIGF0IHRoZSBiZWdpbm5pbmcuIEEgYGRhdGEgZnJhbWVgIGlzIHNpbXBseSBkYXRhIGFycmFuZ2VkIGFzIGEgdGFibGUgb2Ygcm93cyBhbmQgY29sdW1ucy4KCmBgYHtyfQpwb3B1bGF0aW9uCmBgYAoKV2UgZGVmaW5lIGEgdmFyaWFibGUgY2FsbGVkIGBzYW1wbGVfc2l6ZWAgYW5kIGdpdmUgaXQgYSB2YWx1ZSBvZiAxMDAuCgpgYGB7cn0Kc2FtcGxlX3NpemUgPC0gMTAwCmBgYAoKV2UgdGhlbiByYW5kb21seSBzYW1wbGUsIGluIHRoaXMgY2FzZSwgYHNhbXBsZV9zaXplYCBudW1iZXIgb2Ygcm93cyBmcm9tIHRoaXMgZGF0YSBmcmFtZToKCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKQpgYGAKCk5leHQgd2UgYG11dGF0ZWAgdGhlIGRhdGEgZnJhbWUgYnkgYWRkaW5nIGEgbmV3IGNvbHVtbiBjYWxsZWQgYHVuaXRfdHhfZWZmZWN0YCB3aGljaCBpcyBkZWZpbmVkIGFzIGBwb3RlbnRpYWxfMWAgLSBgcG90ZW50aWFsXzBgIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHR3byBwb3RlbnRpYWwgb3V0Y29tZXMuICBUaGUgdmVyYiBgbXV0YXRlYCBzaW1wbHkgbWVhbnMgdGhhdCB3ZSdyZSBtb2RpZnlpbmcgb3IgYWRkaW5nIHRvIHRoZSBkYXRhIGZyYW1lIHdpdGggdGhpcyBvcGVyYXRpb24uCgpgYGB7cn0KcG9wdWxhdGlvbiAlPiUKICBzYW1wbGVfbihzYW1wbGVfc2l6ZSkgJT4lCiAgbXV0YXRlKHVuaXRfdHhfZWZmZWN0ID0gcG90ZW50aWFsXzEgLSBwb3RlbnRpYWxfMCkKYGBgCllvdSB3aWxsIG5vdGljZSB0aGF0IHRoaXMgbmV3IGNvbHVtbiBpcyBjcmVhdGVkIGFzIHRoZSBsYXN0IChyaWdodC1tb3N0KSBjb2x1bW4gb2YgdGhpcyBkYXRhIGZyYW1lLgoKTmV4dCwgd2UgYGFycmFuZ2VgIHRoZSByb3dzIG9mIHRoZSBkYXRhIGZyYW1lIGJ5IGBydW5pZihuKCkpYC4KCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpCmBgYAogVGhlIGBydW5pZihuKCkpYCBmdW5jdGlvbiBzaW1wbHkgZ2VuZXJhdGVzIHJhbmRvbSBjaG9zZW4gbnVtYmVycyBiZXR3ZWVuIDAgYW5kIDEgZm9yIGVhY2ggcm93IGluIHRoZSBkYXRhIGZyYW1lLiBgYXJyYW5nZWAgc29ydHMgdGhlIGRhdGEgZnJhbWUgYnkgdGhpcyByb3cgYW5kIHdlIGhhdmUgcm93cyB0aGF0IGFyZSBub3cgaW4gcmFuZG9tIG9yZGVyLgogCiBZb3UgY2FuIHBsYXkgd2l0aCBgcnVuaWYoKWAgYmVsb3cgdG8gc2VlIGhvdyBpdCB3b3JrczoKCmBgYHtyfQpydW5pZigxMCkKYGBgCgpXaHkgZGlkIHdlIG9yZGVyIHRoZSByb3dzIHJhbmRvbWx5PyBXZWxsIHRoaXMgbGVhZHMgdG8gb3VyIG5leHQgc3RlcCBpbiBgbXV0YXRlYC1pbmcgdGhlIGZpcnN0IGhhbGYgb2YgdGhlIHJvd3MgYXMgdHJlYXRtZW50LCBhbmQgdGhlIHNlY29uZCBoYWxmIG9mIHRoZSByb3dzIGFzIGNvbnRyb2wuIFNpbmNlIHRoZXkgYXJlIHJhbmRvbWx5IG9yZGVyZWQsIHRoaXMgd2lsbCBjcmVhdGUgb3VyIHJhbmRvbSBhc3NpZ25tZW50IG9mIHRyZWF0bWVudC4KCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKQpgYGAKClRoZSBgaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIilgIGV4cHJlc3Npb24gdGVsbHMgdXMgdGhhdCBpZiB0aGUgcm93IG51bWJlciBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHRvdGFsIHJvdyBudW1iZXJzIGRpdmlkZWQgYnkgMiwgYXNzaWduIHRoZSB2YWx1ZSBgdHJlYXRtZW50YCBvdGhlcndpc2UgYXNzaWduIHRoZSB2YWx1ZSBgY29udHJvbGAuIFlvdSB3aWxsIG5vdGljZSBhIG5ldyBjb2x1bW4gY2FsbGVkIGBncm91cGAgY3JlYXRlZCBvbiB0aGUgcmlnaHRtb3N0IHNpZGUgb2YgdGhpcyBkYXRhIGZyYW1lLgoKVGhlIG5leHQgYGFycmFuZ2VgIHNpbXBseSBvcmRlcnMgdGhlIHJvd3MgaW4gdGhlIG9yaWdpbmFsIGBpZGAgb3JkZXIsIHRoZXJlJ3Mgbm8gc3BlY2lhbCByZWFzb24gdG8gZG8gdGhpcyBleGNlcHQga2VlcGluZyBvdXIgZGF0YSBmcmFtZSBpbiBvcmRlciBvZiBpZCBudW1iZXJzLiBUaGUgbmV4dCBzdGVwIGRlZmluZXMgc29tZSBtb3JlIGNvbHVtbnMgaW4gb3VyIGRhdGEgZnJhbWUuIAoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkKYGBgCgpTaW1pbGFyIHRvIHRoZSBgdHJlYXRtZW50YCB2cyBgY29udHJvbGAgYXNzaWdubWVudCB0byB0aGUgYGdyb3VwYCBjb2x1bW4sIHRoZXNlIHR3byBuZXcgY29sdW1ucyBkZWZpbmUgdGhlIG9ic2VydmVkIG91dGNvbWUgaW4gdGhlIGB0cmVhdG1lbnRgIGdyb3VwLCBgb2JzZXJ2ZWRfMWAgYW5kIHRoZSBvYnNlcnZlZCBvdXRjb21lIGluIHRoZSBjb250cm9sIGdyb3VwLCBgb2JzZXJ2ZWRfMGAuIFRoZSBsb2dpYyBzaG91bGQgbG9vayBmYW1pbGlhcjoKCmBvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLCBOQV9yZWFsXylgIHN1Z2dlc3RzIHRoYXQgaWYgdGhlIGdyb3VwIGNvbHVtbiBoYXMgdGhlIHZhbHVlIGBjb250cm9sYCB0aGVuIHdlIHNob3VsZCBhc3NpZ24gYG9ic2VydmVkXzBgIHRoZSB2YWx1ZSBvZiB0aGUgcG90ZW50aWFsIG91dGNvbWUgd2l0aG91dCB0cmVhdG1lbnQsIGBwb3RlbnRpYWxfMGAuIE90aGVyd2lzZSwgdGhlIGNvbHVtbiBnZXRzIGFuIGBOQV9yZWFsX2AgdmFsdWUgd2hpY2ggc2ltcGx5IG1lYW5zIHRoZSBkYXRhIGlzIG1pc3Npbmcgc2luY2UgdGhlIGluZGl2aWR1YWwgd2FzIGluIHRoZSBvdGhlciBgdHJlYXRtZW50YCBncm91cC4gVGhlIHNhbWUgYXBwbGllcyB0byB0aGUgY3JlYXRpb24gb2YgdGhlIGBvYnNlcnZlZF8xYCBjb2x1bW4uCgpUaGUgbmV4dCBjaHVuayBvZiBjb2RlIGJlZ2lubmluZyB3aXRoIGBiaW5kX3Jvd3NgIGlzIGZhaXJseSBjb21wbGljYXRlZCBhbmQgbm90IHBhcnRpY3VsYXJseSBlbGVnYW50LiBJIHdpbGwgc2tpcCB0aGUgZGV0YWlsZWQgZXhwbGFuYXRpb24gZm9yIG5vdyBidXQgdGhlIHB1cnBvc2Ugb2YgdGhpcyBzdGVwIGlzIHRvIGNyZWF0ZSBhIHN1bW1hcnkgcm93IHRoYXQgdGFrZXMgdGhlIG1lYW4gb2YgYWxsIHRoZSBudW1lcmljIGNvbHVtbnMgb2YgdGhlIGRhdGEgYW5kIHJvdW5kcyB0aGVtIHRvIHRoZSBmaXJzdCBzaWduaWZpY2FudCBkaWdpdC4gSXQgYWxzbyByZXBsYWNlcyBjb2x1bW5zIHRoYXQgYXJlIG9mIGEgbm9uLW51bWVyaWMgY2hhcmFjdGVyIHR5cGUgYW5kIHJlcGxhY2VzIGl0IHdpbGwgdGhlIHZhbHVlIGA8PFNVTU1BUlk+PmAuIAoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkgJT4lCiAgYmluZF9yb3dzKAogICAgc3VtbWFyaXNlKC4sIGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgICAgIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCB+ICI8PFNVTU1BUlk+PiIpLAogICAgICAgICAgICAgaWQgPSBOQSkpCmBgYAoKTGV0J3MgZG8gYSBzbGlnaHQgbW9kaWZpY2F0aW9uIG9mIHRoZSBjb2RlIGJ5IHRha2luZyBvdXQgdGhlIGBiaW5kX3Jvd3NgIGZ1bmN0aW9uIHRvIGdldCBhbiBpbnR1aXRpb24gb2Ygd2hhdCBtaWdodCBiZSBnb2luZyBvbiBoZXJlOgoKYGBge3J9CnBvcHVsYXRpb24gJT4lCiAgc2FtcGxlX24oc2FtcGxlX3NpemUpICU+JQogIG11dGF0ZSh1bml0X3R4X2VmZmVjdCA9IHBvdGVudGlhbF8xIC0gcG90ZW50aWFsXzApICU+JQogIGFycmFuZ2UocnVuaWYobigpKSkgJT4lCiAgbXV0YXRlKGdyb3VwID0gaWZfZWxzZShyb3dfbnVtYmVyKCkgPD0gbigpIC8gMiwgInRyZWF0bWVudCIsICJjb250cm9sIikpICU+JQogIGFycmFuZ2UoaWQpICU+JQogIG11dGF0ZShvYnNlcnZlZF8wID0gaWZfZWxzZShncm91cCA9PSAiY29udHJvbCIsIHBvdGVudGlhbF8wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSwKICAgICAgICAgb2JzZXJ2ZWRfMSA9IGlmX2Vsc2UoZ3JvdXAgPT0gInRyZWF0bWVudCIsIHBvdGVudGlhbF8xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BX3JlYWxfKSkgJT4lCiAgc3VtbWFyaXNlKC4sIGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgbWVhbiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgcm91bmQsIGRpZ2l0cyA9IDEpLAogICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgaWQgPSBOQSkKYGBgCldlIHNlZSB0aGF0IHdlIGNyZWF0ZSBhIHNpbmdsZSByb3cgdGhhdCBhdmVyYWdlcyB0aGUgdmFsdWVzIGZyb20gZWFjaCBudW1lcmljIGNvbHVtbiAoaWdub3JpbmcgdGhlIG1pc3NpbmcgTkEgdmFsdWVzKSBhbmQgcm91ZG5zIGl0IHRvIGEgc2luZ2xlIHNpZ25pZmljYW50IGRpZ2l0LiBUaGlzIHJvdyBpcyBzaW1wbHkgYWRkZWQgYXMgdGhlIGZpcnN0IHJvdyBpbiB0aGUgZGF0YSBmcmFtZSBhbmQgdGhlbiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGBvYnNlcnZlZF8xYCBhbmQgYG9ic2VydmVkXzBgIGlzIHRha2VuIGFzIHRoZSBgb2JzZXJ2ZWRfYXRlYDoKCmBgYHtyfQpwb3B1bGF0aW9uICU+JQogIHNhbXBsZV9uKHNhbXBsZV9zaXplKSAlPiUKICBtdXRhdGUodW5pdF90eF9lZmZlY3QgPSBwb3RlbnRpYWxfMSAtIHBvdGVudGlhbF8wKSAlPiUKICBhcnJhbmdlKHJ1bmlmKG4oKSkpICU+JQogIG11dGF0ZShncm91cCA9IGlmX2Vsc2Uocm93X251bWJlcigpIDw9IG4oKSAvIDIsICJ0cmVhdG1lbnQiLCAiY29udHJvbCIpKSAlPiUKICBhcnJhbmdlKGlkKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfMCA9IGlmX2Vsc2UoZ3JvdXAgPT0gImNvbnRyb2wiLCBwb3RlbnRpYWxfMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXyksCiAgICAgICAgIG9ic2VydmVkXzEgPSBpZl9lbHNlKGdyb3VwID09ICJ0cmVhdG1lbnQiLCBwb3RlbnRpYWxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQV9yZWFsXykpICU+JQogIGJpbmRfcm93cygKICAgIHN1bW1hcmlzZSguLCBhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIG1lYW4sIG5hLnJtID0gVFJVRSkpICU+JQogICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgZGlnaXRzID0gMSksCiAgICAgICAgICAgICBhY3Jvc3Mod2hlcmUoaXMuY2hhcmFjdGVyKSwgfiAiPDxTVU1NQVJZPj4iKSwKICAgICAgICAgICAgIGlkID0gTkEpKSAlPiUKICBtdXRhdGUob2JzZXJ2ZWRfYXRlID0gb2JzZXJ2ZWRfMSAtIG9ic2VydmVkXzApICU+JQogIGFycmFuZ2UoZGVzYyhyb3dfbnVtYmVyKCkpKSAlPiUKICBzZWxlY3QoLWlkKQpgYGAKQXMgYSBmaW5pc2hpbmcgdG91Y2ggd2UgcmV2ZXJzZSB0aGUgb3JkZXIgb2YgdGhlIHJvd3Mgc28gdGhhdCB0aGUgbmV3bHkgYWRkZWQgc3VtbWFyeSByb3cgaXMgb24gdG9wLCBhbmQgdGhlbiByZW1vdmUgdGhlIGBpZGAgY29sdW1uIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgb2YgdGhlIGRhdGEgZnJhbWUgZGlzcGxheWVkLg==